class_name Player
extends CharacterBody2D

enum State{
	IDLE,
	RUNNING,
	JUMP,
	FALL,
	LANDING,
	WALL_SLIDING,
	WALL_JUMP,
	ATTACK_1,
	ATTACK_2,
	ATTACK_3,
	HURT,
	DYING
}

@export var can_combo := false

const KNOCKBACK_AMOUNT := 512.0

const GROUND_STATES := [State.IDLE,State.RUNNING,State.LANDING,State.ATTACK_1,State.ATTACK_2,State.ATTACK_3]
const RUN_SPEED := 160.0
const JUMP_VELPCITY := -420
const WALL_JUMP_VELOCITY := Vector2(50,-120)

#地面加速度
const FLOOR_ACCELERATION := RUN_SPEED/0.2
#空中加速
const AIR_ACCELERATION := RUN_SPEED/0.02 

var default_gravity := ProjectSettings.get("physics/2d/default_gravity") as float

var is_first_tick := false

var is_combo_requested := false

var pending_damage:Damage

@onready var graphics: Node2D = $Graphics
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var coyote_timer: Timer = $CoyoteTimer
#这里使用这个计时器的目的是让输入跳跃的时候不是一瞬间的而是变成一段时间内的状态，这样可以在这段时间内都可以判定为跳跃，
#使得跳跃中可以再次响应跳跃
@onready var jump_request_timer: Timer = $JumpRequestTimer
@onready var hand_checker: RayCast2D = $Graphics/HandChecker
@onready var foot_checker: RayCast2D = $Graphics/FootChecker
@onready var state_machine: StateMachine = $StateMachine
@onready var states: States = $States


func _unhandled_input(event: InputEvent) -> void:
	#当按下跳跃键的时候开始计时
	if event.is_action_pressed("jump"):
		jump_request_timer.start()
	
	#if event.is_action_released("jump") and velocity.y < JUMP_VELPCITY/4:
		#velocity.y = JUMP_VELPCITY/4
	#这里判定跳跃键松开时，如果高度小于跳跃预设高度的一半就直接设置为一半的高度
	#可以判断松开过快的话就是短跳，达到小跳的目的
	if event.is_action_released("jump") and velocity.y < JUMP_VELPCITY/2.0:
		velocity.y = JUMP_VELPCITY/2.0
	
	if event.is_action_pressed("attack") and can_combo:
		is_combo_requested = true
		
	
func tick_physics(state:State,delta: float) -> void:
	match state:
		State.IDLE:
			move(default_gravity,delta)
		State.RUNNING:
			move(default_gravity,delta)
		State.JUMP:
			move(0.0 if is_first_tick else default_gravity,delta)
		State.FALL:
			move(default_gravity,delta)
		State.LANDING:
			stand(default_gravity,delta)
		State.WALL_SLIDING:
			move(default_gravity/3.0,delta)
			#graphics.scale.x = get_wall_normal().x
		State.WALL_JUMP:
			if state_machine.state_time < 0.1:
				stand(0.0 if is_first_tick else default_gravity,delta)
				graphics.scale.x = get_wall_normal().x
			else:
				move(default_gravity,delta)
		State.ATTACK_1,State.ATTACK_2,State.ATTACK_3:
			stand(default_gravity,delta)
		State.HURT,State.DYING:
			stand(default_gravity,delta)
		
	is_first_tick = false
	
func move(gravity:float,delta:float)->void:
	var direction = Input.get_axis("move_left","move_right")
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
	#设置一个移动加速的效果
	velocity.x =move_toward(velocity.x,direction * RUN_SPEED,acceleration*delta)
	velocity.y += gravity * delta
		
	if not is_zero_approx(direction):
		graphics.scale.x = -1 if direction<0 else 1
		
	move_and_slide()
	
func stand(gravity:float,delta:float):
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
	velocity.x = move_toward(velocity.x, 0.0, acceleration * delta)
	velocity.y += gravity * delta
	move_and_slide()		

		
func get_next_state(state:State)->int:
	var canJump = is_on_floor() or coyote_timer.time_left>0
	var should_jump := canJump and jump_request_timer.time_left > 0
	if should_jump:
		return State.JUMP
		
	if state in GROUND_STATES and not is_on_floor():
		return State.FALL
		
	if states.health == 0 and state!= State.DYING:
		return State.DYING
		
	if pending_damage:
		return State.HURT
	
	var direction := Input.get_axis("move_left","move_right")
	var is_still := is_zero_approx(direction) and is_zero_approx(velocity.y)
	
	match  state:
		State.IDLE:
			if Input.is_action_just_pressed("attack"):
				return State.ATTACK_1
			if not is_still:
				return State.RUNNING
		State.RUNNING:
			if Input.is_action_just_pressed("attack"):
				return State.ATTACK_1
			if is_still:
				return State.IDLE
		State.FALL:
			if is_on_floor():
				# 这里判定当下落中没有使用方向键时进入landing，否则继续在running状态，
				# 防止方向键时出现landing动画
				return State.LANDING if is_still else State.RUNNING
			if is_on_wall() and hand_checker.is_colliding() and foot_checker.is_colliding():
				return State.WALL_SLIDING
		State.JUMP:
			if velocity.y >= 0:
				return State.FALL
		State.LANDING:
			if not is_still: #防止landing时无法移动player
				return State.RUNNING  
			if not animation_player.is_playing():
				return State.IDLE
		State.WALL_SLIDING:
			if jump_request_timer.time_left>0:
				return State.WALL_JUMP
			if is_on_floor():
				return State.IDLE
			if not is_on_wall(): #离开墙面就下落
				return State.FALL
		State.WALL_JUMP:
			animation_player.play("jump")
			velocity = WALL_JUMP_VELOCITY
			velocity.x *= get_wall_normal().x
			jump_request_timer.stop()
			return State.JUMP
		State.ATTACK_1:
			if not animation_player.is_playing():
				return State.ATTACK_2 if is_combo_requested else State.IDLE	
		State.ATTACK_2:
			if not animation_player.is_playing():
				return State.ATTACK_3 if is_combo_requested else State.IDLE
		State.ATTACK_3:
			if not animation_player.is_playing():
				return State.IDLE
		State.HURT:
			if not animation_player.is_playing():
				return State.IDLE

	return StateMachine.CURRENT_STATE
	
func transition_state(from:State,to:State)->void:
	print("[%s] %s => %s"%[Engine.get_physics_frames(),State.keys()[from] if from !=-1 else "<START>",State.keys()[to]])	
	if from not in GROUND_STATES and to in GROUND_STATES:
		coyote_timer.stop()
	
	match to:
		State.IDLE:
			animation_player.play("idle")
		State.RUNNING:
			animation_player.play("running")
		State.JUMP:
			animation_player.play("jump")
			velocity.y = JUMP_VELPCITY
			coyote_timer.start()
			jump_request_timer.stop() #使得跳跃变成一个时间过程状态
		State.FALL:
			animation_player.play("fall")
			if from in GROUND_STATES:
				coyote_timer.start()
		State.LANDING:
			animation_player.play("landing")
		State.WALL_SLIDING:
			animation_player.play("wall_sliding")
		State.ATTACK_1:
			animation_player.play("attack_1")
			is_combo_requested = false
		State.ATTACK_2:
			animation_player.play("attack_2")
			is_combo_requested = false
		State.ATTACK_3:
			animation_player.play("attack_3")
			is_combo_requested = false
		State.HURT:
			animation_player.play("hurt")
			states.health -= pending_damage.amount
			var dir:= pending_damage.source.global_position.direction_to(global_position)
			velocity = dir * KNOCKBACK_AMOUNT
				
			pending_damage = null
		State.DYING:
			animation_player.play("die")
				
	is_first_tick = true


func _on_hurtbox_hurt(hitbox: Hitbox) -> void:
	pending_damage = Damage.new()
	pending_damage.amount = 1
	pending_damage.source = hitbox.owner
